using System.Collections;
using System.Collections.Generic;
using System.Linq;
using UnityEngine;

[DisallowMultipleComponent]
public class DungeonBuilder : SingletonMonobehaviour<DungeonBuilder>
{
    public Dictionary<string, Room> dungeonBuilderRoomDictionary = new Dictionary<string, Room>();
    private Dictionary<string, RoomTemplateSO> roomTemplateDictionary = new Dictionary<string, RoomTemplateSO>();
    private List<RoomTemplateSO> roomTemplateList = null;
    private RoomNodeTypeListSO roomNodeTypeList;
    private bool dungeonBuildSuccessful;
    
    private void OnEnable()
    {
        // 将变暗的材料设置为关闭
        GameResources.Instance.dimmedMaterial.SetFloat("Alpha_Slider", 0f);
    }
    
    private void OnDisable()
    {
        // 将变暗的材料设置为完全可见
        GameResources.Instance.dimmedMaterial.SetFloat("Alpha_Slider", 1f);
    }

    protected override void Awake()
    {
        base.Awake();

        // Load the room node type list
        LoadRoomNodeTypeList();
    }

    /// <summary>
    /// 加载房间节点类型列表
    /// </summary>
    private void LoadRoomNodeTypeList()
    {
        roomNodeTypeList = GameResources.Instance.roomNodeTypeList;
    }

    /// <summary>
    /// 生成随机地牢，如果建造地牢，则返回 true，如果失败则返回 false。
    /// </summary>
    public bool GenerateDungeon(DungeonLevelSO currentDungeonLevel)
    {
        roomTemplateList = currentDungeonLevel.roomTemplateList;

        // 将可编写脚本的对象室模板加载到字典中
        LoadRoomTemplatesIntoDictionary();

        dungeonBuildSuccessful = false;
        int dungeonBuildAttempts = 0;

        while (!dungeonBuildSuccessful && dungeonBuildAttempts < Settings.maxDungeonBuildAttempts)
        {
            dungeonBuildAttempts++;

            // 从列表中选择一个随机房间节点图
            RoomNodeGraphSO roomNodeGraph = SelectRandomRoomNodeGraph(currentDungeonLevel.roomNodeGraphList);

            int dungeonRebuildAttemptsForNodeGraph = 0;
            dungeonBuildSuccessful = false;

            // 循环直到成功构建地牢或超过节点图的最大尝试次数
            while (!dungeonBuildSuccessful &&
                   dungeonRebuildAttemptsForNodeGraph <= Settings.maxDungeonRebuildAttemptsForRoomGraph)
            {
                // 清除地牢房间游戏对象和地牢房间字典
                ClearDungeon();

                dungeonRebuildAttemptsForNodeGraph++;

                // 尝试为选定的房间节点图构建随机地牢
                dungeonBuildSuccessful = AttemptToBuildRandomDungeon(roomNodeGraph);
            }


            if (dungeonBuildSuccessful)
            {
                // 实例化房间游戏对象
                InstantiateRoomGameobjects();
            }
        }

        return dungeonBuildSuccessful;
    }

    /// <summary>
    /// 将房间模板加载到字典中
    /// </summary>
    private void LoadRoomTemplatesIntoDictionary()
    {
        // 清除房间模板字典
        roomTemplateDictionary.Clear();

        // 将房间模板列表加载到字典中
        foreach (RoomTemplateSO roomTemplate in roomTemplateList)
        {
            if (!roomTemplateDictionary.ContainsKey(roomTemplate.guid))
            {
                roomTemplateDictionary.Add(roomTemplate.guid, roomTemplate);
            }
            else
            {
                Debug.Log("复制房间模板键入 " + roomTemplateList);
            }
        }
    }

    /// <summary>
    /// 尝试随机构建指定房间的地牢 nodeGraph。如果
    /// 生成了成功的随机布局，否则如果已发现问题，则返回 false，并且
    /// 需要再试一次。
    /// </summary>
    private bool AttemptToBuildRandomDungeon(RoomNodeGraphSO roomNodeGraph)
    {
        // 创建开放房间节点队列
        Queue<RoomNodeSO> openRoomNodeQueue = new Queue<RoomNodeSO>();

        // 从房间节点图中将入口节点添加到房间节点队列中
        RoomNodeSO entranceNode = roomNodeGraph.GetRoomNode(roomNodeTypeList.list.Find(x => x.isEntrance));

        if (entranceNode != null)
        {
            openRoomNodeQueue.Enqueue(entranceNode);
        }
        else
        {
            Debug.Log("No Entrance Node");
            return false; // 地牢未建成
        }

        // 从无房间重叠开始
        bool noRoomOverlaps = true;


        // 处理开放房间节点队列
        noRoomOverlaps = ProcessRoomsInOpenRoomNodeQueue(roomNodeGraph, openRoomNodeQueue, noRoomOverlaps);

        // 如果所有房间节点都已处理，并且没有房间重叠，则返回 true
        if (openRoomNodeQueue.Count == 0 && noRoomOverlaps)
        {
            return true;
        }
        else
        {
            return false;
        }
    }

    /// <summary>
    /// 处理打开的房间节点队列中的房间，如果没有房间重叠，则返回 true
    /// </summary>
    private bool ProcessRoomsInOpenRoomNodeQueue(RoomNodeGraphSO roomNodeGraph, Queue<RoomNodeSO> openRoomNodeQueue,
        bool noRoomOverlaps)
    {
        // 虽然房间节点在打开房间节点队列中，但未检测到房间重叠。
        while (openRoomNodeQueue.Count > 0 && noRoomOverlaps == true)
        {
            // 从打开的房间节点队列中获取下一个房间节点。
            RoomNodeSO roomNode = openRoomNodeQueue.Dequeue();

            // 从房间节点图将子节点添加到队列中（带有指向此父房间的链接）
            foreach (RoomNodeSO childRoomNode in roomNodeGraph.GetChildRoomNodes(roomNode))
            {
                openRoomNodeQueue.Enqueue(childRoomNode);
            }

            // 如果房间是入口标记，则按位置添加至房间字典
            if (roomNode.roomNodeType.isEntrance)
            {
                RoomTemplateSO roomTemplate = GetRandomRoomTemplate(roomNode.roomNodeType);

                Room room = CreateRoomFromRoomTemplate(roomTemplate, roomNode);

                room.isPositioned = true;

                // 将房间添加到房间词典
                dungeonBuilderRoomDictionary.Add(room.id, room);
            }

            // 否则，如果房型不是入口
            else
            {
                // 否则获取节点的父房间
                Room parentRoom = dungeonBuilderRoomDictionary[roomNode.parentRoomNodeIDList[0]];

                // 看看是否可以在不重叠的情况下放置空间
                noRoomOverlaps = CanPlaceRoomWithNoOverlaps(roomNode, parentRoom);
            }
        }

        return noRoomOverlaps;
    }

    /// <summary>
    /// 尝试将房间节点放置在地牢中 - 如果可以放置房间，则返回房间，否则返回空
    /// </summary>
    private bool CanPlaceRoomWithNoOverlaps(RoomNodeSO roomNode, Room parentRoom)
    {
        // 初始化并假设重叠，直到证明另有说明。
        bool roomOverlaps = true;

        // 当房间重叠时做 - 尝试放置在父级的所有可用门口，直到
        // 房间已成功放置，没有重叠。
        while (roomOverlaps)
        {
            // 为父级选择随机未连接的可用门口
            List<Doorway> unconnectedAvailableParentDoorways =
                GetUnconnectedAvailableDoorways(parentRoom.doorWayList).ToList();

            if (unconnectedAvailableParentDoorways.Count == 0)
            {
                // 如果没有更多的门道可以尝试，那么重叠失败。
                return false; // 房间重叠
            }

            Doorway doorwayParent =
                unconnectedAvailableParentDoorways[
                    UnityEngine.Random.Range(0, unconnectedAvailableParentDoorways.Count)];

            // 获取与父门方向一致的房间节点的随机房间模板
            RoomTemplateSO roomtemplate = GetRandomTemplateForRoomConsistentWithParent(roomNode, doorwayParent);

            // 创建房间
            Room room = CreateRoomFromRoomTemplate(roomtemplate, roomNode);

            // 放置房间 - 如果房间不重叠，则返回 true
            if (PlaceTheRoom(parentRoom, doorwayParent, room))
            {
                // 如果房间不重叠，则设置为 false 以退出 while 循环
                roomOverlaps = false;

                // 将房间标记为已放置
                room.isPositioned = true;

                // 为词典添加空间
                dungeonBuilderRoomDictionary.Add(room.id, room);
            }
            else
            {
                roomOverlaps = true;
            }
        }

        return true; // 没有房间重叠
    }

    /// <summary>
    /// 获取房间节点的随机房间模板，同时考虑父门口方向。
    /// </summary>
    private RoomTemplateSO GetRandomTemplateForRoomConsistentWithParent(RoomNodeSO roomNode, Doorway doorwayParent)
    {
        RoomTemplateSO roomtemplate = null;

        // 如果房间节点是走廊，则根据随机选择正确的走廊房间模板
        // 家长门口方向
        if (roomNode.roomNodeType.isCorridor)
        {
            switch (doorwayParent.orientation)
            {
                case Orientation.north:
                case Orientation.south:
                    roomtemplate = GetRandomRoomTemplate(roomNodeTypeList.list.Find(x => x.isCorridorNS));
                    break;


                case Orientation.east:
                case Orientation.west:
                    roomtemplate = GetRandomRoomTemplate(roomNodeTypeList.list.Find(x => x.isCorridorEW));
                    break;


                case Orientation.none:
                    break;

                default:
                    break;
            }
        }
        // 否则选择随机房间模板
        else
        {
            roomtemplate = GetRandomRoomTemplate(roomNode.roomNodeType);
        }


        return roomtemplate;
    }

    /// <summary>
    /// 放置房间 - 如果房间不重叠，则返回 true，否则返回 false
    /// </summary>
    private bool PlaceTheRoom(Room parentRoom, Doorway doorwayParent, Room room)
    {
        // 获取当前房间门口位置
        Doorway doorway = GetOppositeDoorway(doorwayParent, room.doorWayList);

        // 如果与父母门口对面的房间没有门口，则返回
        if (doorway == null)
        {
            // 只需将父门口标记为不可用，这样我们就不会再次尝试连接它
            doorwayParent.isUnavailable = true;

            return false;
        }

        // 计算“世界”网格父门口位置
        Vector2Int parentDoorwayPosition =
            parentRoom.lowerBounds + doorwayParent.position - parentRoom.templateLowerBounds;

        Vector2Int adjustment = Vector2Int.zero;

        // 根据我们尝试连接的房间门口位置计算调整位置偏移（例如，如果这个门口在西边，那么我们需要将 （1,0） 添加到东边的父门口）

        switch (doorway.orientation)
        {
            case Orientation.north:
                adjustment = new Vector2Int(0, -1);
                break;

            case Orientation.east:
                adjustment = new Vector2Int(-1, 0);
                break;

            case Orientation.south:
                adjustment = new Vector2Int(0, 1);
                break;

            case Orientation.west:
                adjustment = new Vector2Int(1, 0);
                break;

            case Orientation.none:
                break;

            default:
                break;
        }

        // 根据位置计算房间的下限和上限，以与父门口对齐
        room.lowerBounds = parentDoorwayPosition + adjustment + room.templateLowerBounds - doorway.position;
        room.upperBounds = room.lowerBounds + room.templateUpperBounds - room.templateLowerBounds;

        Room overlappingRoom = CheckForRoomOverlap(room);

        if (overlappingRoom == null)
        {
            // 将门口标记为已连接和不可用
            doorwayParent.isConnected = true;
            doorwayParent.isUnavailable = true;

            doorway.isConnected = true;
            doorway.isUnavailable = true;

            // 返回真实，以显示房间已连接，无重叠
            return true;
        }
        else
        {
            // 只需将父门口标记为不可用，这样我们就不会再次尝试连接它
            doorwayParent.isUnavailable = true;

            return false;
        }
    }

    /// <summary>
    /// 从门口列表中获取与门口方向相反的门口
    /// </summary>
    private Doorway GetOppositeDoorway(Doorway parentDoorway, List<Doorway> doorwayList)
    {
        foreach (Doorway doorwayToCheck in doorwayList)
        {
            if (parentDoorway.orientation == Orientation.east && doorwayToCheck.orientation == Orientation.west)
            {
                return doorwayToCheck;
            }
            else if (parentDoorway.orientation == Orientation.west && doorwayToCheck.orientation == Orientation.east)
            {
                return doorwayToCheck;
            }
            else if (parentDoorway.orientation == Orientation.north && doorwayToCheck.orientation == Orientation.south)
            {
                return doorwayToCheck;
            }
            else if (parentDoorway.orientation == Orientation.south && doorwayToCheck.orientation == Orientation.north)
            {
                return doorwayToCheck;
            }
        }

        return null;
    }

    /// <summary>
    /// 检查与上限和下限参数重叠的房间，如果存在重叠的房间，则返回房间否则返回 null
    /// </summary>
    private Room CheckForRoomOverlap(Room roomToTest)
    {
        // 遍历所有房间
        foreach (KeyValuePair<string, Room> keyvaluepair in dungeonBuilderRoomDictionary)
        {
            Room room = keyvaluepair.Value;

            // 如果与要测试的房间位于同一房间或未定位房间，则跳过
            if (room.id == roomToTest.id || !room.isPositioned)
                continue;

            // 如果房间重叠
            if (IsOverLappingRoom(roomToTest, room))
            {
                return room;
            }
        }

        // Return
        return null;
    }

    /// <summary>
    /// 检查 2 个房间是否相互重叠 - 如果它们重叠，则返回 true，如果它们不重叠，则返回 false
    /// </summary>
    private bool IsOverLappingRoom(Room room1, Room room2)
    {
        bool isOverlappingX = IsOverLappingInterval(room1.lowerBounds.x, room1.upperBounds.x, room2.lowerBounds.x,
            room2.upperBounds.x);

        bool isOverlappingY = IsOverLappingInterval(room1.lowerBounds.y, room1.upperBounds.y, room2.lowerBounds.y,
            room2.upperBounds.y);

        if (isOverlappingX && isOverlappingY)
        {
            return true;
        }
        else
        {
            return false;
        }
    }
    
    /// <summary>
    /// 检查间隔 1 是否与间隔 2 重叠 - 此方法由 IsOverlappingRoom 方法使用
    /// </summary>
    private bool IsOverLappingInterval(int imin1, int imax1, int imin2, int imax2)
    {
        if (Mathf.Max(imin1, imin2) <= Mathf.Min(imax1, imax2))
        {
            return true;
        }
        else
        {
            return false;
        }
    }

    /// <summary>
    /// 从 roomtemplatelist 中获取一个与 roomType 匹配的随机房间模板，并返回它
    /// （如果未找到匹配的房间模板，则返回 null）。
    /// </summary>
    private RoomTemplateSO GetRandomRoomTemplate(RoomNodeTypeSO roomNodeType)
    {
        List<RoomTemplateSO> matchingRoomTemplateList = new List<RoomTemplateSO>();

        // 循环遍历房间模板列表
        foreach (RoomTemplateSO roomTemplate in roomTemplateList)
        {
            // 添加匹配的房间模板
            if (roomTemplate.roomNodeType == roomNodeType)
            {
                matchingRoomTemplateList.Add(roomTemplate);
            }
        }

        // 如果列表为零，则返回 null
        if (matchingRoomTemplateList.Count == 0)
            return null;

        // 从列表中选择随机房间模板并返回
        return matchingRoomTemplateList[UnityEngine.Random.Range(0, matchingRoomTemplateList.Count)];
    }

    /// <summary>
    /// 获取未连接的门口
    /// </summary>
    private IEnumerable<Doorway> GetUnconnectedAvailableDoorways(List<Doorway> roomDoorwayList)
    {
        // 循环访问门口列表
        foreach (Doorway doorway in roomDoorwayList)
        {
            if (!doorway.isConnected && !doorway.isUnavailable)
                yield return doorway;
        }
    }

    /// <summary>
    /// 基于 roomTemplate 和 layoutNode 创建房间，并返回创建的房间
    /// </summary>
    private Room CreateRoomFromRoomTemplate(RoomTemplateSO roomTemplate, RoomNodeSO roomNode)
    {
        // 从模板初始化房间
        Room room = new Room();

        room.templateID = roomTemplate.guid;
        room.id = roomNode.id;
        room.prefab = roomTemplate.prefab;
        room.battleMusic = roomTemplate.battleMusic;
        room.ambientMusic = roomTemplate.ambientMusic;
        room.roomNodeType = roomTemplate.roomNodeType;
        room.lowerBounds = roomTemplate.lowerBounds;
        room.upperBounds = roomTemplate.upperBounds;
        room.spawnPositionArray = roomTemplate.spawnPositionArray;
        room.enemiesByLevelList = roomTemplate.enemiesByLevelList;
        room.roomLevelEnemySpawnParametersList = roomTemplate.roomEnemySpawnParametersList;
        room.templateLowerBounds = roomTemplate.lowerBounds;
        room.templateUpperBounds = roomTemplate.upperBounds;
        room.childRoomIDList = CopyStringList(roomNode.childRoomNodeIDList);
        room.doorWayList = CopyDoorwayList(roomTemplate.doorwayList);

        // 为房间设置父类 ID
        if (roomNode.parentRoomNodeIDList.Count == 0) // Entrance
        {
            room.parentRoomID = "";
            room.isPreviouslyVisited = true;

            // 在游戏管理器中设置入口
            GameManager.Instance.SetCurrentRoom(room);
        }
        else
        {
            room.parentRoomID = roomNode.parentRoomNodeIDList[0];
        }


        // 如果没有要生成的敌人，则默认房间没有敌人
        if (room.GetNumberOfEnemiesToSpawn(GameManager.Instance.GetCurrentDungeonLevel()) == 0)
        {
            room.isClearedOfEnemies = true;
        }


        return room;
    }

    /// <summary>
    /// 从房间节点图列表中选择一个随机房间节点图
    /// </summary>
    private RoomNodeGraphSO SelectRandomRoomNodeGraph(List<RoomNodeGraphSO> roomNodeGraphList)
    {
        if (roomNodeGraphList.Count > 0)
        {
            return roomNodeGraphList[UnityEngine.Random.Range(0, roomNodeGraphList.Count)];
        }
        else
        {
            Debug.Log("列表中没有房间节点图");
            return null;
        }
    }

    /// <summary>
    /// 创建字符串列表的深层副本
    /// </summary>
    private List<string> CopyStringList(List<string> oldStringList)
    {
        List<string> newStringList = new List<string>();

        foreach (string stringValue in oldStringList)
        {
            newStringList.Add(stringValue);
        }

        return newStringList;
    }

    /// <summary>
    /// 创建门口列表的深度副本
    /// </summary>
    private List<Doorway> CopyDoorwayList(List<Doorway> oldDoorwayList)
    {
        List<Doorway> newDoorwayList = new List<Doorway>();

        foreach (Doorway doorway in oldDoorwayList)
        {
            Doorway newDoorway = new Doorway();

            newDoorway.position = doorway.position;
            newDoorway.orientation = doorway.orientation;
            newDoorway.doorPrefab = doorway.doorPrefab;
            newDoorway.isConnected = doorway.isConnected;
            newDoorway.isUnavailable = doorway.isUnavailable;
            newDoorway.doorwayStartCopyPosition = doorway.doorwayStartCopyPosition;
            newDoorway.doorwayCopyTileWidth = doorway.doorwayCopyTileWidth;
            newDoorway.doorwayCopyTileHeight = doorway.doorwayCopyTileHeight;

            newDoorwayList.Add(newDoorway);
        }

        return newDoorwayList;
    }
    
    /// <summary>
    /// 从预制件中实例化地牢房间游戏对象
    /// </summary>
    private void InstantiateRoomGameobjects()
    {
        // 遍历所有地牢房间。
        foreach (KeyValuePair<string, Room> keyvaluepair in dungeonBuilderRoomDictionary)
        {
            Room room = keyvaluepair.Value;
        
            // 计算房间位置（记住房间实例化位置需要通过房间模板的下限来调整）
            Vector3 roomPosition = new Vector3(room.lowerBounds.x - room.templateLowerBounds.x, room.lowerBounds.y - room.templateLowerBounds.y, 0f);
        
            // 实例化房间
            GameObject roomGameobject = Instantiate(room.prefab, roomPosition, Quaternion.identity, transform);
        
            // 从实例化的预制件中获取实例化的房间组件。
            InstantiatedRoom instantiatedRoom = roomGameobject.GetComponentInChildren<InstantiatedRoom>();
        
            instantiatedRoom.room = room;
        
            // 初始化实例化房间
            instantiatedRoom.Initialise(roomGameobject);
        
            // 保存游戏对象引用。
            room.instantiatedRoom = instantiatedRoom;
        
            //// 演示代码，将房间设置为已清理 - BOSS除外
            //if (!room.roomNodeType.isBossRoom)
            //{
            //    room.isClearedOfEnemies = true;
            //}
        }
    }
    
    /// <summary>
    /// 按房间模板 ID 获取房间模板，如果 ID 不存在，则返回 null
    /// </summary>
    public RoomTemplateSO GetRoomTemplate(string roomTemplateID)
    {
        if (roomTemplateDictionary.TryGetValue(roomTemplateID, out RoomTemplateSO roomTemplate))
        {
            return roomTemplate;
        }
        else
        {
            return null;
        }
    }
    
    /// <summary>
    /// 按 roomID 获取房间，如果不存在具有该 ID 的房间，则返回 null
    /// </summary>
    public Room GetRoomByRoomID(string roomID)
    {
        if (dungeonBuilderRoomDictionary.TryGetValue(roomID, out Room room))
        {
            return room;
        }
        else
        {
            return null;
        }
    }

    /// <summary>
    /// 清除地牢房间游戏对象和地牢房间字典
    /// </summary>
    private void ClearDungeon()
    {
        // 销毁实例化的地牢游戏对象并清除地牢管理员房间字典
        if (dungeonBuilderRoomDictionary.Count > 0)
        {
            foreach (KeyValuePair<string, Room> keyvaluepair in dungeonBuilderRoomDictionary)
            {
                Room room = keyvaluepair.Value;

                if (room.instantiatedRoom != null)
                {
                    Destroy(room.instantiatedRoom.gameObject);
                }
            }

            dungeonBuilderRoomDictionary.Clear();
        }
    }
}